/**
 * 
 */
package com.dalonedrau.jogl.opengl.sprite;

import com.dalonedrau.jogl.engine.ProjectConstants;
import com.dalonedrau.pool.PooledStringBuilder;
import com.dalonedrau.pool.StringBuilderPool;

/**
 * @author Donald
 */
public final class GlyphObjectFactory {
	/**
	 * the one and only instance of the <code>GlyphFactory</code> class.
	 */
	private static GlyphObjectFactory	instance;
	/**
	 * Gives access to the singleton instance of {@link GlyphObjectFactory}.
	 * @return {@link GlyphObjectFactory}
	 */
	public static GlyphObjectFactory getInstance() {
		if (GlyphObjectFactory.instance == null) {
			GlyphObjectFactory.instance = new GlyphObjectFactory();
		}
		return GlyphObjectFactory.instance;
	}
	/** the character map. */
	private char[]		charMap;
	/** the list of font sheets available. */
	private String[]	fontMap;
	/** 
	 * the map of all glyphs - each index in this two-dimensional array is an 
	 * array of image reference ids. 
	 */
	private int[][]		imageMap;
	/** Hidden constructor. */
	private GlyphObjectFactory() {
		fontMap = new String[ProjectConstants.getInstance().getNumGameFonts()];
		imageMap = new int[ProjectConstants.getInstance().getNumGameFonts()][];
		charMap = new char[] {
				'a', 'b', 'c', 'd', 'e', 'f', 'g', 
				'A', 'B', 'C', 'D', 'E', 'F', 'G',
				'h', 'i', 'j', 'k', 'l', 'm', 'n',
				'H', 'I', 'J', 'K', 'L', 'M', 'N',
				'o', 'p', 'q', 'r', 's', 't', 'u',
				'O', 'P', 'Q', 'R', 'S', 'T', 'U',
				'v', 'w', 'x', 'y', 'z', ' ', ' ',
				'V', 'W', 'X', 'Y', 'Z', ' ', ' ',
				'1', '2', '3', '4', '5', '6', '7', '8', '9', '0', ' ', ' ', ' ',
				' ',
				'.', ':', ',', ';', '\'', '"', '(', '!', '?', ')', ' ', ' ', 
				' ', ' ',
				'+', '-', '*', '/', '=', ' ', ' ',
				'~', '`', '@', '#', '$', '%', '^',
				'&', '[', ']', '{', '}', '|', '\\'
				};
	}
	/**
	 * Adds a glyph to the font sheets.
	 * @param fontName the name of the font
	 * @param glyph the glyph the image represents
	 * @param imgRef the image's reference id
	 * @throws Exception if an error occurs
	 */
	public void addGlyph(final String fontName, final char glyph,
			final int imgRef) throws Exception {
		int index = 0;
		// check to see if font has been started.
		for (; index < fontMap.length; index++) {
			if (fontMap[index] == null) {
				// we have an empty slot. use this one
				fontMap[index] = fontName;
				imageMap[index] = new int[charMap.length];
				break;
			} else if (fontName.equalsIgnoreCase(fontMap[index])) {
				break;
			}
		}
		if (index >= fontMap.length) {
			// create temp array to store names
			String[] tempNames = new String[index + 1];
			// copy all font names into temp array
			System.arraycopy(fontMap, 0, tempNames, 0, fontMap.length);
			tempNames[index] = fontName;
			// create temp array to image ref ids
			int[][] tempImages =
					new int[index + 1][];
			// copy all image ref ids into temp array
			for (int j = 0; j < fontMap.length; j++) {
				tempImages[j] = new int[charMap.length];
				System.arraycopy(
						imageMap[j], 0, tempImages[j], 0, imageMap[j].length);
			}
			tempImages[index] = new int[charMap.length];
			fontMap = tempNames;
			imageMap = tempImages;
		}
		// a new map has been created. add image to map
		int charIndex = 0;
		for (; charIndex < charMap.length; charIndex++) {
			if (charMap[charIndex] == glyph) {
				break;
			}
		}
		if (charIndex >= charMap.length) {
			throw new Exception("invalid character in mapping");
		}
		// store image for character
		imageMap[index][charIndex] = imgRef;
	}
	/**
	 * Adds a glyph to the font sheets.
	 * @param fontName the name of the font
	 * @param glyph the glyph the image represents
	 * @param image the glyph image
	 * @throws Exception if an error occurs
	 */
	public void addGlyph(final String fontName, final char glyph,
			final SpriteImageObject image) throws Exception {
		addGlyph(fontName, glyph, image.getRefId());
	}
	/**
	 * Gets a specific {@link SpriteImageObject} by its reference character and
	 * font.
	 * @param fontName the name of the font
	 * @param glyph the character
	 * @return {@link SpriteImageObject}
	 * @throws Exception if the reference name isn't found
	 */
	public SpriteImageObject getGlyph(final String fontName, final char glyph)
			throws Exception {
		int index = 0;
		// check to see if font has been started.
		for (; index < fontMap.length; index++) {
			if (fontMap[index] != null
					&& fontMap[index].equalsIgnoreCase(fontName)) {
				break;
			}
		}
		if (index > fontMap.length) {
			PooledStringBuilder sb =
					StringBuilderPool.getInstance().getStringBuilder();
			sb.append("GlyphFactory Error - ");
			sb.append("font ");
			sb.append(fontName);
			sb.append(" does not exist.");
			Exception ex = new Exception(sb.toString());
			sb.returnToPool();
			throw ex;
		}
		// font has been found. find glyph.
		int charIndex = 0;
		for (; charIndex < charMap.length; charIndex++) {
			if (charMap[charIndex] == glyph) {
				break;
			}
		}
		if (charIndex >= charMap.length) {
			PooledStringBuilder sb =
					StringBuilderPool.getInstance().getStringBuilder();
			sb.append("GlyphFactory Error - ");
			sb.append("invalid character '");
			sb.append(glyph);
			sb.append("'.");
			Exception ex = new Exception(sb.toString());
			sb.returnToPool();
			throw ex;
		}
		SpriteImageObject image = SpriteImageObjectFactory.getInstance()
				.getImageById(
						imageMap[index][charIndex]);
		if (image == null) {
			PooledStringBuilder sb =
					StringBuilderPool.getInstance().getStringBuilder();
			sb.append("GlyphFactory Error - ");
			sb.append("character '");
			sb.append(glyph);
			sb.append("' does not have an associated image.");
			Exception ex = new Exception(sb.toString());
			sb.returnToPool();
			throw ex;
		}
		return image;
	}
}
